home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
ka9q
/
nhclb120.zoo
/
telnet.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-08-19
|
18KB
|
834 lines
#include <stdio.h>
#include <ctype.h>
#include "config.h"
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "icmp.h"
#include "netuser.h"
#include "tcp.h"
#include "telnet.h"
#include "session.h"
#include "ftp.h"
#include "iface.h"
#include "ax25.h"
#include "lapb.h"
#include "finger.h"
#include "nr4.h"
#ifdef UNIX
#include <string.h>
#endif
#define CTLZ 26
#if !defined( _MEMORY_H) && !defined(_STRING_H)
char *memchr();
#endif
static free_telnet();
static void answer();
extern char nospace[];
extern char badhost[];
int refuse_echo = 0;
int unix_line_mode = 0; /* if true turn <cr> to <nl> when in line mode */
int debug_options = 0;
extern FILE *trfp; /* file pointer used for tracing */
void suboption();
char *t_options[] = {
"BINARY", "ECHO", "RCP", "SGA", "NAMS", "STATUS", "TM",
"RCTE", "NAOL", "NAOP", "NAOCRD", "NAOHTS", "NAOHTD", "NAOFFD",
"NAOVTS", "NAOVTD", "NAOLFD", "XASCII", "LOGOUT", "BM", "DET",
"SUPDUP", "SUPDUPOUOT", "SNDLOC", "TTYPE", "EOR", "TACACS",
"OUTMARK", "LOCNUM", "TN3270", "X3PAD", "NAWS", "TSPEED",
"LFLOW", "LINEMODE", "XDISPLOC"
};
/* Execute user telnet command */
int
dotelnet(argc,argv)
int argc;
char *argv[]; /* argv[1] = host, argv[2] = service */
{
void t_state(),rcv_char(),tn_tx();
char *inet_ntoa();
int32 resolve();
int send_tel();
int unix_send_tel();
struct session *s;
struct telnet *tn;
struct tcb *tcb;
struct socket lsocket,fsocket;
lsocket.address = ip_addr;
lsocket.port = lport++;
printf("Lookup ...");
if((fsocket.address = resolve(argv[1])) == 0){
printf(badhost,argv[1]);
return 1;
}
if(argc < 3)
fsocket.port = TELNET_PORT;
else
fsocket.port = service_code(argv[2]);
if(fsocket.port<0){
printf("Unknown service %s\n", argv[2]);
return(1);
}
printf(" done. Trying to connect to %s ...\n",
inet_ntoa( fsocket.address));
/* Allocate a session descriptor */
if((s = newsession()) == NULLSESSION){
printf("Too many sessions\n");
return 1;
}
if((s->name = malloc((unsigned)strlen(argv[1])+1)) != NULLCHAR)
strcpy(s->name,argv[1]);
s->type = TELNET;
if ((refuse_echo == 0) && (unix_line_mode != 0)) {
s->parse = unix_send_tel;
} else {
s->parse = send_tel;
}
current = s;
/* Create and initialize a Telnet protocol descriptor */
if((tn = (struct telnet *)calloc(1,sizeof(struct telnet))) == NULLTN){
printf(nospace);
s->type = FREE;
return 1;
}
tn->session = s; /* Upward pointer */
tn->state = TS_DATA;
s->cb.telnet = tn; /* Downward pointer */
tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,0,
rcv_char,tn_tx,t_state,0,(char *)tn);
tn->tcb = tcb; /* Downward pointer */
go_mode();
return 0;
}
/* Process typed characters */
int
unix_send_tel(buf,n)
char *buf;
int16 n;
{
int i;
for (i=0; (i<n) && (buf[i] != '\r'); i++)
;
if (buf[i] == '\r') {
buf[i] = '\n';
n = i+1;
}
send_tel(buf,n);
}
int
send_tel(buf,n)
char *buf;
int16 n;
{
struct mbuf *bp;
if(current == NULLSESSION || current->cb.telnet == NULLTN
|| current->cb.telnet->tcb == NULLTCB)
return;
/* If we're doing our own echoing and recording is enabled, record it */
if(!current->cb.telnet->remote[TN_ECHO] && current->record != NULLFILE)
fwrite(buf,1,(int)n,current->record);
bp = qdata(buf,n);
send_tcp(current->cb.telnet->tcb,bp);
}
/* set up correct tty modes */
int
tel_setterm(tn)
register struct telnet *tn;
{
if(current->cb.telnet->remote[TN_ECHO]) {
raw(); /* other end is echoing, we're raw */
if (tn->lflow)
flowon();
else
flowoff();
} else {
cooked();
flowdefault();
}
}
/*
* stdio has no way to flush. So do our own.
*/
/* might as well make the buffer big enough for an Ethernet packet */
#define TBSIZE 2048
char termbuf[TBSIZE];
char *termbufp = termbuf;
char *termbufe = termbuf + TBSIZE;
int
tnputchar(c)
int c;
{
if (termbufp >= termbufe) {
write(1, termbuf, TBSIZE);
termbufp = termbuf;
}
*termbufp++ = c;
}
/* send out buffered data */
int
tnflush()
{
if (termbufp > termbuf)
write(1, termbuf, termbufp - termbuf);
termbufp = termbuf;
}
/* clear output -- should also do clearout to do Unix clear */
int
tnclear()
{
termbufp = termbuf;
}
/* Process incoming TELNET characters */
int
tel_input(tn,bp)
register struct telnet *tn;
struct mbuf *bp;
{
char c;
void doopt(),dontopt(),willopt(),wontopt(),answer();
FILE *record;
/* Optimization for very common special case -- no special chars */
if(tn->state == TS_DATA){
while(bp != NULLBUF && memchr(bp->data,IAC,(int)bp->cnt) == NULLCHAR){
if (! tn->outsup) {
if((record = tn->session->record) != NULLFILE)
fwrite(bp->data,1,(int)bp->cnt,record);
while(bp->cnt-- != 0)
tnputchar(*bp->data++);
}
bp = free_mbuf(bp);
}
}
while(pullup(&bp,&c,1) == 1){
switch(tn->state){
case TS_DATA:
if(uchar(c) == IAC){
tn->state = TS_IAC;
} else {
#ifdef undef
if(!tn->remote[TN_TRANSMIT_BINARY])
c &= 0x7f;
#endif
if (! tn->outsup) {
tnputchar(c);
if((record = tn->session->record)
!= NULLFILE)
putc(c,record);
}
}
break;
case TS_IAC:
process_iac:
switch(uchar(c)){
case WILL:
tn->state = TS_WILL;
break;
case WONT:
tn->state = TS_WONT;
break;
case DO:
tn->state = TS_DO;
break;
case DONT:
tn->state = TS_DONT;
break;
case TN_DM:
/*
* if outsup > 1, we are still in urgent
* data, so DM is ignored
*/
if (tn->outsup == 1) {
tn->outsup = 0;
if (debug_options)
fprintf(trfp, "[End of urgent data]\n");
}
tn->state = TS_DATA;
break;
case SB:
SB_CLEAR();
tn->state = TS_SB;
break;
case IAC:
tnputchar(c);
tn->state = TS_DATA;
break;
default:
tn->state = TS_DATA;
break;
}
break;
case TS_WILL:
willopt(tn,c);
tn->state = TS_DATA;
break;
case TS_WONT:
wontopt(tn,c);
tn->state = TS_DATA;
break;
case TS_DO:
doopt(tn,c);
tn->state = TS_DATA;
break;
case TS_DONT:
dontopt(tn,c);
tn->state = TS_DATA;
break;
case TS_SB:
if (uchar(c) == IAC) {
tn->state = TS_SE;
break;
} else {
SB_ACCUM(c);
}
break;
case TS_SE:
if (uchar(c) != SE) {
if (uchar(c) != IAC) {
/*
* This is an error. We only expect to get
* "IAC IAC" or "IAC SE". Several things may
* have happend. An IAC was not doubled, the
* IAC SE was left off, or another option got
* inserted into the suboption are all possibilities.
* If we assume that the IAC was not doubled,
* and really the IAC SE was left off, we could
* get into an infinate loop here. So, instead,
* we terminate the suboption, and process the
* partial suboption if we can.
*/
SB_ACCUM(IAC);
SB_ACCUM(c);
tn->subpointer -= 2;
SB_TERM();
suboption(tn); /* handle sub-option */
tn->state = TS_IAC;
goto process_iac;
}
SB_ACCUM(c);
tn->state = TS_SB;
} else {
SB_ACCUM(IAC);
SB_ACCUM(SE);
tn->subpointer -= 2;
SB_TERM();
suboption(tn); /* handle sub-option */
tn->state = TS_DATA;
}
break;
}
}
}
/* Telnet receiver upcall routine */
void
rcv_char(tcb,cnt)
register struct tcb *tcb;
int16 cnt;
{
struct mbuf *bp;
struct telnet *tn;
FILE *record;
#ifdef FLOW
extern int ttyflow;
#endif
if((tn = (struct telnet *)tcb->user) == NULLTN){
/* Unknown connection; ignore it */
return;
}
/* Hold output if we're not the current session */
if(mode != CONV_MODE || current == NULLSESSION
#ifdef FLOW
|| !ttyflow /* Or if blocked by keyboard input -- hyc */
#endif
|| current->type != TELNET || current->cb.telnet != tn)
return;
/*
* Urgent data is done by combination of the TCP level and this code.
* The TCP level sets URGCUR and tcb->up (urgent pointer), but we
* clear it at this level. We want to clear it when we've taken
* data beyond the urgent pointer, and only here are we in a position
* to control that. The code is better here anyway, because other
* applications may treat the urgent pointer differently